<template>
  <div class="comparison-page">
    <div class="page-header">
      <h1>Transformer vs. LSTM 模型性能对比</h1>
      <p>上传包含预测结果的JSON文件，直观地比较两种模型的性能表现，并通过量化性能指标深度评估模型优劣。</p>
    </div>

    <div class="content-body">
      <div class="data-visualization-panel">
        <h2><i class="fas fa-chart-line"></i> 预测结果可视化</h2>

        <div v-if="!jsonData" class="upload-container">
          <div class="upload-area" @click="triggerFileInput">
            <i class="fas fa-cloud-upload-alt"></i>
            <p>点击上传对比数据 (.json)</p>
            <span>(请使用包含 "actual_value", "transformer_predicted_value", "lstm_adaboost_predicted_value"
              字段的JSON文件)</span>
            <input type="file" ref="fileInput" @change="handleFileChange" style="display: none;"
              accept="application/json" />
          </div>
        </div>

        <div v-if="jsonData" class="chart-container">
          <div class="file-info-bar">
            <div class="file-details">
              <i class="fas fa-file-alt"></i>
              <span>{{ selectedFile.name }} ({{ formatFileSize(selectedFile.size) }})</span>
            </div>
            <button class="clear-btn" @click="clearData">
              <i class="fas fa-trash-alt"></i> 清空数据并重新上传
            </button>
          </div>
          <div class="canvas-wrapper">
            <canvas ref="chartCanvas"></canvas>
          </div>
        </div>
      </div>

      <div v-if="metrics.transformer" class="metrics-panel">
        <h2><i class="fas fa-tasks"></i> 模型性能量化对比</h2>
        <div class="metrics-table-container">
          <table class="metrics-table">
            <thead>
              <tr>
                <th>评估指标 (Metric)</th>
                <th>Transformer</th>
                <th>LSTM</th>
                <th class="winner-col">优胜者</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="metric in metricOrder" :key="metric.key">
                <td>{{ metric.name }}</td>
                <td>{{ formatMetric(metrics.transformer[metric.key]) }}</td>
                <td>{{ formatMetric(metrics.lstm[metric.key]) }}</td>
                <td class="winner-col">
                  <span class="badge" :class="getWinner(metric.key).badgeClass">
                    <i :class="getWinner(metric.key).icon"></i>
                    {{ getWinner(metric.key).name }}
                  </span>
                </td>
              </tr>
            </tbody>
          </table>
        </div>

        <div class="conclusion-panel">
          <h3><i class="fas fa-flag-checkered"></i> 性能评估结论</h3>
          <p>
            根据上述量化指标分析，<strong class="transformer-winner">Transformer 模型</strong> 在本次预测任务中表现出全面的优越性。
          </p>
          <ul>
            <li>
              <strong>误差更小:</strong> Transformer 的 <strong>MSE ({{ formatMetric(metrics.transformer.mse) }})</strong> 和
              <strong>MAE ({{ formatMetric(metrics.transformer.mae) }})</strong> 指标均远低于 LSTM，表明其预测值与真实值的偏差更小，预测结果更精确。
            </li>
            <li>
              <strong>鲁棒性更强:</strong> <strong>RMSE ({{ formatMetric(metrics.transformer.rmse) }})</strong>
              指标对异常值（即大的预测误差）更敏感。Transformer 在此项上的优势说明其模型预测更加稳定，不易受个别极端数据点的影响。
            </li>
            <li>
              <strong>拟合度更高:</strong> <strong>R² 分数 ({{ formatMetric(metrics.transformer.r2) }})</strong> 接近 1，而 LSTM 的
              R² 分数较低。这决定性地证明了 Transformer 模型能更好地解释数据中的变异性，对真实数据的拟合程度远超对手。
            </li>
            <li>
              <strong>百分比误差更低:</strong> 在 <strong>MAPE</strong> 和 <strong>SMAPE</strong> 两个相对误差指标上，Transformer
              同样表现出色，证明其预测误差在不同量级的数据点上都能保持较低水平。
            </li>
          </ul>
          <p class="summary">
            综上所述，无论是从绝对误差、相对误差还是数据拟合度的角度，Transformer 模型都展现了其作为先进时间序列预测模型的强大能力。
          </p>
        </div>
      </div>

      <div class="model-info-panel">
        <div class="model-intro transformer">
          <h2><i class="fas fa-exchange-alt"></i> 关于 Transformer 模型</h2>
          <p>
            Transformer 模型最初在自然语言处理（NLP）领域取得巨大成功，其核心是 <strong>自注意力机制 (Self-Attention
              Mechanism)</strong>。与依赖顺序处理的循环神经网络（RNN）不同，Transformer 能够并行处理整个数据序列，这使其在处理长序列时效率更高。
          </p>
          <ul>
            <li><strong>并行计算</strong>: 自注意力机制使其可以同时关注序列中的所有位置，极大地提升了训练速度。</li>
            <li><strong>长距离依赖捕捉</strong>: 它能更有效地捕捉序列中相距较远元素之间的依赖关系，解决了 LSTM 等模型在处理超长序列时可能遇到的信息遗忘问题。</li>
            <li><strong>应用拓展</strong>: 近年来，Transformer 已被成功应用于时间序列预测、计算机视觉等多个领域，展现出强大的通用性。</li>
          </ul>
        </div>
        <div class="model-intro lstm">
          <h2><i class="fas fa-long-arrow-alt-right"></i> 关于 LSTM 模型</h2>
          <p>
            长短期记忆网络（LSTM）是一种特殊的循环神经网络（RNN），旨在解决标准 RNN 中的 <strong>梯度消失和梯度爆炸</strong> 问题。它通过引入精妙的“门”结构来控制信息的流动。
          </p>
          <ul>
            <li><strong>门控机制</strong>: LSTM
              拥有三个关键的门——<strong>遗忘门、输入门和输出门</strong>。这些门可以选择性地让信息通过，决定哪些旧信息被遗忘，哪些新信息被记忆，以及在当前时间步输出什么。</li>
            <li><strong>记忆能力</strong>: 这种结构使得 LSTM 非常擅长学习和记忆时间序列数据中的长期依赖关系，是处理时序问题的经典模型。</li>
            <li><strong>序列处理</strong>: 作为 RNN 的一种，LSTM 按顺序处理数据点，这使其在理解具有时间依赖性的动态变化方面具有天然优势。</li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Chart from 'chart.js/auto';

export default {
  name: 'ModelComparison',
  data() {
    return {
      selectedFile: null,
      jsonData: null,
      chartInstance: null,
      metrics: {},
      metricOrder: [
        { key: 'mse', name: 'MSE (均方误差)' },
        { key: 'rmse', name: 'RMSE (均方根误差)' },
        { key: 'mae', name: 'MAE (平均绝对误差)' },
        { key: 'mape', name: 'MAPE (%)' },
        { key: 'smape', name: 'SMAPE (%)' },
        { key: 'r2', name: 'R² Score (决定系数)' },
      ]
    };
  },
  methods: {
    triggerFileInput() {
      this.$refs.fileInput.click();
    },
    handleFileChange(e) {
      const file = e.target.files[0];
      if (!file) {
        this.clearData();
        return;
      }

      if (file.type !== 'application/json') {
        alert('请上传JSON格式的文件！');
        this.clearData();
        return;
      }

      this.selectedFile = file;
      const reader = new FileReader();

      reader.onload = (event) => {
        try {
          const parsedData = JSON.parse(event.target.result);
          // Validate JSON file for necessary prediction fields
          if (!parsedData.actual_value || !parsedData.transformer_predicted_value || !parsedData.lstm_adaboost_predicted_value) {
            alert('JSON文件缺少必要的预测值字段！请检查文件内容。');
            this.clearData();
            return;
          }
          this.jsonData = parsedData;
          this.calculateAllMetrics(); // Calculate metrics after data is loaded
          this.$nextTick(() => {
            this.renderChart();
          });
        } catch (error) {
          console.error('解析JSON文件失败:', error);
          alert('上传的文件不是有效的JSON格式！');
          this.clearData();
        }
      };

      reader.onerror = (error) => {
        console.error('读取文件失败:', error);
        alert('读取文件失败！');
        this.clearData();
      };

      reader.readAsText(file);
    },
    calculateAllMetrics() {
      const actual = this.jsonData.actual_value;
      const transformerPred = this.jsonData.transformer_predicted_value;
      const lstmPred = this.jsonData.lstm_adaboost_predicted_value;

      this.metrics = {
        transformer: this.computeMetrics(actual, transformerPred),
        lstm: this.computeMetrics(actual, lstmPred)
      };
    },
    computeMetrics(actual, predicted) {
      const n = actual.length;
      let mse = 0, mae = 0, mape = 0, smape = 0;
      const actualMean = actual.reduce((a, b) => a + b, 0) / n;
      let ssr = 0, sst = 0; // Sum of squares of residuals, Total sum of squares

      for (let i = 0; i < n; i++) {
        const error = actual[i] - predicted[i];
        mse += error * error;
        mae += Math.abs(error);

        if (actual[i] !== 0) {
          mape += Math.abs(error / actual[i]);
        }

        const smape_denominator = Math.abs(actual[i]) + Math.abs(predicted[i]);
        if (smape_denominator !== 0) {
          smape += Math.abs(error) / smape_denominator;
        }

        ssr += error * error; // For R2 calculation, this is sum of squared residuals
        sst += (actual[i] - actualMean) * (actual[i] - actualMean); // Total sum of squares
      }

      mse /= n;
      const rmse = Math.sqrt(mse);
      mae /= n;
      mape = (mape / n) * 100;
      smape = (smape / n) * 200; // SMAPE formula variation

      let r2;
      if (sst === 0) { // Handle case where actual values are all the same
        r2 = (ssr === 0) ? 1 : 0; // If residuals are also 0, perfect fit, otherwise 0.
      } else {
        r2 = 1 - (ssr / sst);
      }

      return { mse, rmse, mae, mape, smape, r2 };
    },
    getWinner(metricKey) {
      if (!this.metrics.transformer || !this.metrics.lstm) return { name: '', badgeClass: '', icon: '' };

      const transformerScore = this.metrics.transformer[metricKey];
      const lstmScore = this.metrics.lstm[metricKey];

      // For R2 Score, higher is better. For all others (errors), lower is better.
      const isHigherBetter = metricKey === 'r2';

      if (transformerScore === undefined || lstmScore === undefined) {
        return { name: 'N/A', badgeClass: '', icon: '' };
      }

      if (isNaN(transformerScore) || isNaN(lstmScore)) {
        // Handle cases where one or both scores might be NaN
        if (isNaN(transformerScore) && isNaN(lstmScore)) return { name: 'N/A', badgeClass: '', icon: '' };
        if (isNaN(transformerScore)) return { name: 'LSTM', badgeClass: 'badge-lstm', icon: 'fas fa-medal' };
        if (isNaN(lstmScore)) return { name: 'Transformer', badgeClass: 'badge-transformer', icon: 'fas fa-trophy' };
      }

      if ((isHigherBetter && transformerScore > lstmScore) || (!isHigherBetter && transformerScore < lstmScore)) {
        return { name: 'Transformer', badgeClass: 'badge-transformer', icon: 'fas fa-trophy' };
      } else if ((isHigherBetter && lstmScore > transformerScore) || (!isHigherBetter && lstmScore < transformerScore)) {
        return { name: 'LSTM', badgeClass: 'badge-lstm', icon: 'fas fa-medal' };
      } else {
        return { name: '并列', badgeClass: 'badge-tie', icon: 'fas fa-equals' }; // Handle ties
      }
    },
    formatMetric(value) {
      if (typeof value !== 'number' || isNaN(value)) return 'N/A';
      // R2 score needs more precision
      if (value >= 0.9 || (value <= 0.1 && value >= -0.1 && value !== 0)) return value.toFixed(4);
      return value.toFixed(2);
    },
    renderChart() {
      if (this.chartInstance) {
        this.chartInstance.destroy();
      }

      if (!this.jsonData || !this.$refs.chartCanvas) return;

      const { actual_value, transformer_predicted_value, lstm_adaboost_predicted_value, metadata } = this.jsonData;
      // Ensure labels array length matches the data length
      const dataLength = Math.max(actual_value.length, transformer_predicted_value.length, lstm_adaboost_predicted_value.length);
      const labels = Array.from({ length: dataLength }, (_, i) => i + 1);


      const ctx = this.$refs.chartCanvas.getContext('2d');
      this.chartInstance = new Chart(ctx, {
        type: 'line',
        data: {
          labels: labels,
          datasets: [
            {
              label: '真实值 (Actual Value)',
              data: actual_value,
              borderColor: 'rgba(54, 162, 235, 1)',
              backgroundColor: 'rgba(54, 162, 235, 0.2)', // Added for better fill
              borderWidth: 2.5,
              pointRadius: 1,
              tension: 0.1
            },
            {
              label: 'Transformer 预测值',
              data: transformer_predicted_value,
              borderColor: 'rgba(255, 99, 132, 1)',
              backgroundColor: 'rgba(255, 99, 132, 0.2)',
              borderWidth: 2,
              pointRadius: 1,
              tension: 0.4
            },
            {
              label: 'LSTM 预测值',
              data: lstm_adaboost_predicted_value,
              borderColor: 'rgba(75, 192, 192, 1)',
              backgroundColor: 'rgba(75, 192, 192, 0.2)',
              borderWidth: 2,
              pointRadius: 1,
              tension: 0.4
            }
          ]
        },
        options: {
          responsive: true,
          maintainAspectRatio: false,
          plugins: {
            title: {
              display: true,
              text: (metadata && metadata.title) ? metadata.title : '模型预测结果对比',
              font: { size: 18, weight: 'bold' },
              padding: { top: 10, bottom: 20 }
            },
            legend: {
              position: 'top',
              labels: { font: { size: 14 } }
            },
            tooltip: { // Added for better tooltip experience
              mode: 'index',
              intersect: false,
            }
          },
          scales: {
            x: {
              title: {
                display: true,
                text: (metadata && metadata.xlabel) ? metadata.xlabel : '时间步',
                font: { size: 14 }
              }
            },
            y: {
              title: {
                display: true,
                text: (metadata && metadata.ylabel) ? metadata.ylabel : '数值',
                font: { size: 14 }
              }
            }
          }
        }
      });
    },
    clearData() {
      if (this.chartInstance) {
        this.chartInstance.destroy();
      }
      this.selectedFile = null;
      this.jsonData = null;
      this.chartInstance = null;
      this.metrics = {}; // Clear metrics as well
      if (this.$refs.fileInput) {
        this.$refs.fileInput.value = ''; // Reset file input
      }
    },
    formatFileSize(bytes) {
      if (bytes === 0) return '0 Bytes';
      const k = 1024;
      const sizes = ['Bytes', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
  },
  beforeUnmount() {
    if (this.chartInstance) {
      this.chartInstance.destroy();
    }
  }
}
</script>

<style scoped>
/* General Styles */
.comparison-page {
  width: 100%;
  max-width: 1400px;
  margin: auto;
  padding: 20px;
  box-sizing: border-box;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  background-color: #f8f9fa;
  color: #343a40;
}

.page-header {
  text-align: center;
  margin-bottom: 30px;
}

.page-header h1 {
  font-size: 28px;
  font-weight: 700;
  margin-bottom: 10px;
}

.page-header p {
  font-size: 16px;
  color: #6c757d;
}

.content-body {
  display: flex;
  flex-direction: column;
  gap: 30px;
}

h2 {
  font-size: 22px;
  /* Adjusted to be slightly larger */
  font-weight: 600;
  margin-bottom: 20px;
  /* Increased margin */
  display: flex;
  align-items: center;
  gap: 12px;
  /* Increased gap for icon */
  color: #0056b3;
  /* A more prominent blue */
}

h3 {
  font-size: 18px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 10px;
  color: #17a2b8;
  /* A teal color for subheadings */
}

/* Data Visualization Panel */
.data-visualization-panel,
.metrics-panel {
  /* Combined styles for panels */
  background: white;
  border-radius: 12px;
  /* Slightly more rounded */
  padding: 25px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.07);
  /* Enhanced shadow */
}

.upload-container {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 40px 0;
}

.upload-area {
  border: 2px dashed #007bff;
  border-radius: 10px;
  /* Slightly more rounded */
  padding: 50px 70px;
  /* Increased padding */
  text-align: center;
  cursor: pointer;
  transition: all 0.3s ease-in-out;
  /* Smoother transition */
  background-color: #fefefe;
  /* Lighter background */
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  /* Subtle shadow */
}

.upload-area:hover {
  border-color: #0056b3;
  background-color: #e9f5ff;
  /* Lighter blue on hover */
  transform: translateY(-3px);
  /* Slight lift effect */
  box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
  /* More pronounced shadow on hover */
}

.upload-area i {
  font-size: 50px;
  /* Larger icon */
  color: #007bff;
  margin-bottom: 20px;
  /* Increased margin */
}

.upload-area p {
  font-size: 20px;
  /* Larger text */
  font-weight: 600;
  /* Bolder text */
  color: #343a40;
  margin: 0;
}

.upload-area span {
  font-size: 15px;
  /* Slightly larger hint text */
  color: #6c757d;
  margin-top: 5px;
  /* Added margin */
  display: block;
}

/* Chart Container */
.chart-container {
  display: flex;
  flex-direction: column;
}

.file-info-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  padding: 12px 18px;
  /* Adjusted padding */
  background-color: #e9f5ff;
  border-radius: 8px;
  /* Slightly more rounded */
  border: 1px solid #cce5ff;
  /* Subtle border */
}

.file-details {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 15px;
  /* Slightly larger text */
  color: #0056b3;
  font-weight: 500;
}

.clear-btn {
  background: #dc3545;
  color: white;
  border: none;
  border-radius: 6px;
  /* Slightly more rounded */
  padding: 9px 18px;
  /* Adjusted padding */
  cursor: pointer;
  font-size: 14px;
  transition: background-color 0.2s ease, transform 0.1s ease;
  display: flex;
  align-items: center;
  gap: 8px;
}

.clear-btn:hover {
  background: #c82333;
  transform: translateY(-1px);
}

.canvas-wrapper {
  position: relative;
  height: 50vh;
  min-height: 450px;
  /* Min height from test.vue */
}

/* Metrics Panel */
.metrics-table-container {
  overflow-x: auto;
  margin-top: 20px;
}

.metrics-table {
  width: 100%;
  border-collapse: collapse;
  text-align: center;
  min-width: 600px;
  /* Ensure table doesn't get too narrow on smaller screens */
}

.metrics-table th,
.metrics-table td {
  padding: 15px;
  border-bottom: 1px solid #dee2e6;
  font-size: 16px;
}

.metrics-table th {
  background-color: #f1f3f5;
  font-weight: 600;
  position: sticky;
  top: 0;
  z-index: 1;
  /* For sticky header */
}

.metrics-table tbody tr:last-child td {
  border-bottom: none;
}

.metrics-table tbody tr:hover {
  background-color: #f8f9fa;
}

.metrics-table td:nth-child(2) {
  /* Transformer column */
  font-weight: 700;
  color: #dc3545;
  /* Reddish for Transformer */
}

.metrics-table td:nth-child(3) {
  /* LSTM column */
  font-weight: 700;
  color: #28a745;
  /* Greenish for LSTM */
}

.winner-col {
  width: 150px;
}

.badge {
  padding: 7px 14px;
  /* Adjusted padding */
  border-radius: 25px;
  /* More rounded */
  color: white;
  font-weight: bold;
  font-size: 13px;
  /* Slightly smaller text in badge */
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* Increased gap */
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  /* Subtle shadow for badges */
}

.badge i {
  font-size: 1em;
  /* Ensure icon scales with text */
}

.badge-transformer {
  background-color: #dc3545;
}

.badge-lstm {
  background-color: #28a745;
}

.badge-tie {
  background-color: #ffc107;
  color: #343a40;
}

/* Yellow for tie */

/* Conclusion Panel */
.conclusion-panel {
  margin-top: 30px;
  padding: 25px;
  /* Increased padding */
  background-color: #e6f7ff;
  /* Lighter blue */
  border-left: 5px solid #007bff;
  /* Consistent blue border */
  border-radius: 8px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
  /* Subtle shadow */
}

.conclusion-panel h3 {
  margin-bottom: 15px;
}

.conclusion-panel p {
  font-size: 16px;
  line-height: 1.7;
  color: #495057;
}

.conclusion-panel ul {
  list-style-type: none;
  padding-left: 0;
  margin-top: 15px;
}

.conclusion-panel li {
  padding-left: 30px;
  /* More space for checkmark */
  position: relative;
  margin-bottom: 15px;
  /* More space between list items */
  color: #343a40;
}

.conclusion-panel li::before {
  content: '✓';
  color: #28a745;
  font-weight: bold;
  font-size: 22px;
  /* Larger checkmark */
  position: absolute;
  left: 0;
  top: -2px;
}

.conclusion-panel strong {
  color: #0056b3;
  /* Highlight important text in blue */
}

.conclusion-panel .transformer-winner {
  color: #dc3545;
  /* Specifically highlight transformer winner in its color */
  font-size: 1.1em;
}

.conclusion-panel .summary {
  margin-top: 25px;
  /* More margin */
  font-weight: 600;
  /* Bolder summary */
  color: #007bff;
  /* Blue for summary */
  border-top: 1px dashed #cce5ff;
  /* Separator line */
  padding-top: 15px;
}

/* Model Info Panel (from test1.vue) */
.model-info-panel {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 30px;
  margin-top: 30px;
  /* Added margin to separate from metrics */
}

.model-intro {
  background: white;
  border-radius: 12px;
  /* Consistent border radius */
  padding: 25px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  border-left: 5px solid;
}

.model-intro.transformer {
  border-color: #ff6384;
}

.model-intro.transformer h2 {
  color: #ff6384;
}

.model-intro.lstm {
  border-color: #4bc0c0;
}

.model-intro.lstm h2 {
  color: #4bc0c0;
}

.model-intro p {
  font-size: 16px;
  line-height: 1.7;
  color: #495057;
  margin-bottom: 15px;
}

.model-intro ul {
  list-style: none;
  padding-left: 0;
}

.model-intro ul li {
  font-size: 15px;
  line-height: 1.6;
  padding-left: 20px;
  position: relative;
  margin-bottom: 10px;
}

.model-intro ul li::before {
  content: '✓';
  position: absolute;
  left: 0;
  font-weight: bold;
}

.model-intro.transformer ul li::before {
  color: #ff6384;
}

.model-intro.lstm ul li::before {
  color: #4bc0c0;
}


@media (max-width: 992px) {
  .model-info-panel {
    grid-template-columns: 1fr;
  }
}

/* Responsive adjustments for smaller screens */
@media (max-width: 768px) {
  .page-header h1 {
    font-size: 24px;
  }

  .page-header p {
    font-size: 14px;
  }

  h2 {
    font-size: 20px;
    margin-bottom: 15px;
  }

  .upload-area {
    padding: 30px 40px;
  }

  .upload-area i {
    font-size: 40px;
  }

  .upload-area p {
    font-size: 16px;
  }

  .upload-area span {
    font-size: 13px;
  }

  .file-info-bar {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .clear-btn {
    width: 100%;
    justify-content: center;
  }

  .metrics-table th,
  .metrics-table td {
    padding: 10px;
    font-size: 14px;
  }

  .badge {
    padding: 5px 10px;
    font-size: 12px;
  }

  .conclusion-panel {
    padding: 18px;
  }

  .conclusion-panel p,
  .conclusion-panel li {
    font-size: 15px;
  }
}
</style>